#include <linux/kernel.h> //用到了malloc(), free()
#include <stdarg.h>       //用到了va_start()等和vsprintf()
#include <linux/sched.h>  //遍历进程是仿照schedule函数，其在sched.c中
#include <asm/segment.h>  //使用了put_fs_byte

#define MAX_NUM 1024 // malloc申请的内存空间不超过1页;

/* 仿照 printf 函数实现 sprintf：将格式化数据写入缓冲区
 * 成功返回写入buf的字符个数，失败返回-1
 */
// extern int vsprintf(char * buf, const char * fmt, va_list args);
int sprintf(char *buf, const char *fmt, ...)
{
    va_list args;
    int i;
    va_start(args, fmt);
    i = vsprintf(buf, fmt, args);
    va_end(args);
    return i;
}

int proc_read(int dev, char *buf, int count, unsigned long *pos)
{
    /* 由于buf是用户段的地址空间，所以内核段还得申请一段地址空间
     * 之后再把内核段数据传输到用户段的地址空间
     */
    char *proc_buf = NULL;
    int printf_num = 0;          // 接收sprintf的返回值，表征输出的字符个数;
    int tmp_pn;                  // 可能sprintf会返回-1
    unsigned long offset = *pos; // 默认位置为 0 
    int output_num = 0;          // 根据 output_num 来更新 pos；
    struct task_struct **p;      // 为了遍历系统所有进程

    switch (dev)
    {
    case 0:
        /** 处理psinfo文件 **/
        proc_buf = (char *)malloc(sizeof(char) * MAX_NUM);
        printf_num = sprintf(proc_buf, "pid\tstate\tfather\tcounter\tstart_time\n");
        /* 遍历系统的所有进程（参照schedule函数的处理）*/
        for (p = &FIRST_TASK; p <= &LAST_TASK; p++)
        {
            if (*p)
            {
                tmp_pn = sprintf(proc_buf + printf_num, "%3d\t%5d\t%6d\t%7d\t%10d\n",
                                 (*p)->pid, (*p)->state, (*p)->father, (*p)->counter, (*p)->start_time);
                if (tmp_pn < 0)
                {
                    // proc_buf 空间不够，需要增大MAX_NUM
                    return -1;
                }
                printf_num += tmp_pn;
            }
        }
        *(proc_buf + printf_num) = '\0';
        break;
    default:
        break;
    }
    /* 将内核段数据 proc_buf 传输到用户段的地址空间 buf*/
    while (count > 0)
    {
        if (offset > printf_num)
            break;
        put_fs_byte(*(proc_buf + offset), buf++);
        count--;
        if (*(proc_buf + offset) == '\0')
            break;
        offset++;
        output_num++;
    }

    (*pos) += output_num;

    free(proc_buf);
    return output_num;
}
